package bblvolume

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"runtime/debug"
	"strings"
)

type VolumeMux struct {
	fs StorageSystem
	vs *VolumeServer
}

func NewVolumeMux(fs StorageSystem, v *VolumeServer) *VolumeMux {
	return &VolumeMux{fs: fs, vs: v}
}

var (
	op = map[string]HandlerFunc{
		"OPEN":     do_open,
		"CLOSE":    do_close,
		"WRITE":    do_write,
		"APPEND":   do_append,
		"READ":     do_read,
		"HEAD":     do_read,
		"GET":      do_read,
		"DELETE":   do_delete,
		"TRUNCATE": do_truncate,
	}
)

type HandlerFunc func(fs StorageSystem, rw http.ResponseWriter, req *http.Request)

func notFound(rw http.ResponseWriter, req *http.Request) {
	http.NotFound(rw, req)
}
func badRequest(rw http.ResponseWriter, req *http.Request) {
	http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
}
func internalServerError(rw http.ResponseWriter, req *http.Request) {
	http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}

func is_multipart(r *http.Request) bool {
	ct := r.Header.Get("Content-Type")
	return strings.HasPrefix(ct, "multipart") || strings.HasPrefix(ct, "Multipart")
}

func (m *VolumeMux) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	log.Debug(m.vs.UUID, m.vs.TranAddr, req.Method, req.URL.String())
	defer func() {
		if e := recover(); e != nil {
			log.Error(string(debug.Stack()))
			internalServerError(rw, req)
		}
	}()
	rw.Header().Set("Server", server_name())
	rw.Header().Set("Server-Name", m.vs.Name)
	if handler, exist := op[req.Method]; exist {
		handler(m.fs, rw, req)
	} else {
		badRequest(rw, req)
	}
}

var open_objects = map[string]Object{}

func do_open(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
	obj, err := fs.Open(req.URL.Path, os.O_CREATE|os.O_APPEND|os.O_RDWR)
	if err != nil {
		notFound(rw, req)
		log.Error(err)
		return
	}
	open_objects[req.URL.Path] = obj
}

func do_append(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
	if obj, exist := open_objects[req.URL.Path]; !exist {
		badRequest(rw, req)
		log.Error(req.URL.Path, "not opened")
		return
	} else {
		writen, err := io.Copy(obj, req.Body)
		if err != nil {
			internalServerError(rw, req)
			log.Error(err)
			return
		}
		// for _, addr := range fs.ClusterServer() {
		// 	err = ACK("ACK_APPEND", fmt.Sprintf("http://%v%v", addr, req.URL.Path), fmt.Sprintf("{\"size\":%v}", writen))
		// 	if err == nil {
		// 		break
		// 	}
		// 	log.Error(err)
		// }
		resp := fmt.Sprintf("{\"code\":0,\"size\":%v}", writen)
		rw.Write([]byte(resp))
	}
}

func do_close(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
	if obj, exist := open_objects[req.URL.Path]; exist {
		obj.Close()
		delete(open_objects, req.URL.Path)
	}
}

func do_read(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
	obj, err := fs.Open(req.URL.Path, os.O_RDONLY)
	if err != nil {
		notFound(rw, req)
		log.Error(err)
		return
	}
	defer obj.Close()
	stat, err := obj.Stat()
	if err != nil {
		internalServerError(rw, req)
		log.Error(err)
		return
	}
	//sizeFunc := func() (int64, error) { return d.Size(), nil }
	http.ServeContent(rw, req, stat.Name(), stat.ModTime(), obj)
}
func do_write(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
	obj, err := fs.Open(req.URL.Path, os.O_CREATE|os.O_TRUNC|os.O_RDWR)
	if err != nil {
		internalServerError(rw, req)
		log.Error(err)
		return
	}
	defer obj.Close()

	if is_multipart(req) {
		file, _, err := req.FormFile("object")
		if err != nil {
			internalServerError(rw, req)
			log.Error(err)
			return
		}
		defer file.Close()
		writen, err := io.Copy(obj, file)
		if err != nil {
			internalServerError(rw, req)
			log.Error(err)
			return
		}
		rw.Header().Set("X-WRITTEN-SIZE", fmt.Sprintf("%v", writen))
		resp := fmt.Sprintf("{\"code\":0,\"size\":%v}", writen)
		rw.Write([]byte(resp))
		log.Debug("Write Object", fs.UUID, obj.Name(), writen, "bytes")
	} else {
		writen, err := io.Copy(obj, req.Body)
		if err != nil {
			log.Error("io.Copy(obj, req.Body),Written", writen, err)
			//internalServerError(rw, req)
			return
		}
		rw.Header().Set("X-WRITTEN-SIZE", fmt.Sprintf("%v", writen))
		resp := fmt.Sprintf("{\"code\":0,\"size\":%v}", writen)
		rw.Write([]byte(resp))
		log.Debug("Write Object", fs.UUID, obj.Name(), writen, "bytes")
	}

}

func do_delete(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
	if !fs.Exist(req.URL.Path) {
		notFound(rw, req)
		return
	}
	err := fs.Remove(req.URL.Path)
	if err != nil {
		internalServerError(rw, req)
		log.Error(err)
	}
}
func do_truncate(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
	obj, err := fs.Open(req.URL.Path, os.O_RDWR)
	if err != nil {
		notFound(rw, req)
		log.Error(err)
		return
	}
	defer obj.Close()
	err = obj.Truncate(0)
	if err != nil {
		internalServerError(rw, req)
		log.Error(err)
	}
}

// func do_stat(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
// 	obj, err := fs.Find(req.URL.Path)
// 	if err != nil {
// 		notFound(rw, req)
// 		log.Error(err)
// 		return
// 	}
// 	defer obj.Close()
// 	stat, err := obj.Stat()
// 	if err != nil {
// 		internalServerError(rw, req)
// 		log.Error(err)
// 		return
// 	}
// 	rw.Write()
// }

// //本地或者远程移动文件
// func do_move(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
// 	to := req.Header.Get("X-MOVE-TO")
// 	if strings.HasPrefix(to, "/") || strings.HasPrefix(to, "http://") {
// 		if err := fs.Rename(req.URL.Path, to); err != nil {
// 			internalServerError(rw, req)
// 			log.Error(err)
// 			return
// 		}
// 		for _, addr := range fs.ClusterServer() {
// 			err = ACK("ACK_MOVE", fmt.Sprintf("http://%v%v", addr, req.URL.Path), fmt.Sprintf("{\"X-MOVE-TO\":%v}", to))
// 			if err == nil {
// 				break
// 			}
// 			log.Error(err)
// 		}
// 	} else {
// 		badRequest(rw, req)
// 	}
// }

// //本地或者远程COPY文件
// func do_copy(fs StorageSystem, rw http.ResponseWriter, req *http.Request) {
// 	to := req.Header.Get("X-COPY-TO")
// 	if strings.HasPrefix(to, "/") || strings.HasPrefix(to, "http://") {
// 		if err := fs.Copy(req.URL.Path, to); err != nil {
// 			internalServerError(rw, req)
// 			log.Error(err)
// 			return
// 		}
// 		for _, addr := range fs.ClusterServer() {
// 			err = ACK("ACK_COPY", fmt.Sprintf("http://%v%v", addr, req.URL.Path), fmt.Sprintf("{\"X-COPY-TO\":%v}", to))
// 			if err == nil {
// 				break
// 			}
// 			log.Error(err)
// 		}
// 	} else {
// 		badRequest(rw, req)
// 	}
// }
